/**  
 * Copyright (c) 2010 University of Pennsylvania.
 *     All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS
 *  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 *  express or implied.  See the License for the specific language
 *  governing permissions and limitations under the License.
 *
 */

//#define USER_TOPOLOGY                    // if defined, topology is restricted by user-defined array, used for debugging
#ifdef USER_TOPOLOGY
struct {
  ADDRESS n1, n2;                             // list of directional links, and additional artificial loss per link (on top of real radio loss) 0-100%
  uint8_t loss_perc;                          // if a link is not present, it is assumed to have 0% loss
} PACKING_MACRO user_top[] = {
  {0, 2, 100}
};
#endif

#define CMD_COUNT                 14     // low-level message types
#define CMD_MASK                0x0f     // bits to use for command id

#define CMD_DISCOVERY              0     // discover neighbors
#define CMD_TREECONSTRUCT          1     // build routing tree, broadcast hop count
#define CMD_TREECOMMIT             2     // once tree established, signal beginning of summary aggregation
#define CMD_TREESUMMARY            3     // send summary on all attributes
#define CMD_TREESUMMARYUPDATE      4     // update summary on one attribute
#define CMD_TREEROOTNOTIFY         5     // notify root of next tree (if any)
#define CMD_SEQROUTE               6     // FindFirst request forward (exploration)
#define CMD_SEQROUTEUNR            7     // FindFirst request backward (backtrack)
#define CMD_FLOODROUTE             8     // BestRoute requests
#define CMD_BASEFLOOD              9     // Flood from base station
#define CMD_FORWARD               10     // Source routing (greedy forward using a path vector)
#define CMD_FORWARDBUFFEREXCHANGE 11     // exchange flow information between neighbors
#define CMD_PATHSUBSCRIBE         12     // request path from a neighbor
#define CMD_PATHRESPOND           13     // respond to a path request

#ifdef MINIMUM_MEMORY
  #define OUTPUTBUFFER_SIZE          15  // size of physical message outgoing queue
  #define FRAGMENTBUFFER_SIZE         5  // size of physical incoming queue for fragmented messages
  #define MAX_PEERLINKS              15  // maximum number of neighbors
  #define EXTNEIGHBORHOODBUFFER_SIZE 20  // size of extended neighborhood
  #define DISCOVERYBUFFER_SIZE       15
#else
  #define OUTPUTBUFFER_SIZE          50
  #define FRAGMENTBUFFER_SIZE        10
  #define MAX_PEERLINKS              30
  #define EXTNEIGHBORHOODBUFFER_SIZE 50
  #define DISCOVERYBUFFER_SIZE       30
#endif

#define MAX_FRAGMENTCOUNT    10     // technically it is 16, 4 bits

#define MAX_DISCOVERYMSG        20       // Retry discovery after this many cycles
#define MIN_DISCOVERYMSG         5       // At least that many unicast messages must be received from a peer...
#define MIN_DISCOVERYTIME       (DISCOVERY_EPOCHS-25)       // ...within this amount of time to be considered reliable
#define MIN_DISCOVERYPOWER   (-32)    // with average received power at least this much

#define MIN_MSGPOWER         (-35)   // ignore message with strength below this threshold

#define PEER_DEFAULTPOWER      0xff  // if supplied as radio power: use default peer power 

#define MSG_NOCONFIRM         0     // do not send reliably
#define MSG_CONFIRM           1     // send reliably

#define MIN_MSGLEN            2     // size of CRC

#ifdef EVENLENGTH_MSG
#define MAX_MSGLEN            (TOSH_DATA_LENGTH-1)  // even maximum length
#else
#define MAX_MSGLEN            TOSH_DATA_LENGTH  // OS msg payload
#endif

#define PACKETHEADER_SIZE     5     // maximum overhead of physical layer

#define MAX_GENERICPAYLOAD   (MAX_MSGLEN-PACKETHEADER_SIZE)  // maximum payload of physical layer
#define MAX_PAYLOAD_LONG     MIN(MAX_GENERICPAYLOAD*MAX_FRAGMENTCOUNT, 255) // max payload of fragmentation layer

#define MAX_GROUPSIZE        10     // max number of physical fragments to merge into outgoing message

#define REALNODE_MASK(addr)       ((addr) & 0x7fff)       // Used for virtual addressing broadcast scheme
#define VIRTUALNODE_MASK(addr)    (1 << ((addr) % 15))
#define VIRTUALNODE_ADDR(addr)    (0x8000 | (addr))
#define IS_VIRTUAL(addr)          (((addr) & 0x8000) != 0)

#define PEERLINK_OK       0      // Peer is functioning
#define PEERLINK_IGNORE   1      // Peer is functioning but ignored due to hash collision or something else
#define PEERLINK_FAILED   2      // Peer is not functioning

#define MAX_DUPCOUNT      200

typedef struct {
  uint8_t cmd;                   // indicate discovery message type
  
  uint8_t has_time, is_timesync;
  
  uint32_t time;                 // time at sending the message, updated in update_buffer
  uint16_t main_timer;           // value of main timer interval  
  ADDRESS time_hops;             // hops to closest node whose time we are using (should be Base)
} PACKING_MACRO DISCOVERY_MSG;

typedef struct {
  uint8_t used;                  // entry in use

  ADDRESS id;                    // ID of neighbor
  uint8_t status;                // PEERLINK_XXX value
  uint8_t congestion;            // nonzero if peer is congested
  uint16_t response_time; // how many confirmed messages; averaged response time in cycles
  uint32_t lastmsgrcv_time;              // time of last message received
  
  int8_t rcv_power;
  uint8_t send_power;

  uint8_t sendunicast_confirmseq, rcvunicast_confirmseq;  // outgoing and incoming unicast confirmation sequences
  uint8_t rcvbroadcast_confirmseq;  // incoming broadcast confirmation sequence
  int16_t unicast_msgindex;       // index of last message in output_buffer
  
  uint8_t rcvunicast_dupcount, rcvbroadcast_dupcount;   // number of repeated unicast/broadcast messages (same confirmation id), congestion?
  
  PEERTREE_TYPE tree;             // tree summaries 
} PACKING_MACRO PEER_LINK;

typedef struct {       // physical message
  ADDRESS source_id;   // source ADDRESS (this node)
  uint8_t confirm_seq; // confirmation sequence byte
  int16_t time_ofs;    // if positive: offset of 2-byte timercycles to be updated at msg as update_buffer called
  uint8_t send_power;  // radio power at which to send the message
  uint8_t allow_merge; // allow merging with other messages into one physical packet
  uint8_t msg_len;     // message is confirmation if msg_len = 0
  uint8_t *msg;        // message payload
} PACKING_MACRO GENERIC_MSG;

typedef struct {
  uint8_t used;

  uint16_t retry_count;        // how many times has this been attempted before
  uint32_t lasttry_time;      // when last attepmpted
  int16_t next, prev;         // used for sending non-confirm messages in order of confirm_seq

  GENERIC_MSG msg;            // physical message

  uint8_t integrate_next;     // integrate with following (next) buffer entry on release
  uint8_t fragment_msglen;    // integrated message from previously sent fragments
  uint8_t *fragment_msg;      // partial integration of fragments belonging to the same logical message

  uint8_t node_count;         // number of destination neighbors
  ADDRESS *nodes;             // dest. neighbors
} PACKING_MACRO OUTPUTBUFFER_ENTRY;

typedef struct {              // scheduling queue
  int16_t outputbuffer_index;     // index in physical outgoing queue
  uint32_t nexttry_time;      // at what time to send this index
} PACKING_MACRO OUTPUTSCHEDULE_ENTRY;

typedef struct {              // input queue of fragmented physical messages
  uint8_t used;

  ADDRESS source_addr;        // source address of neighbor 
  uint8_t is_broadcast, fragment_index;  // was this broadcast, index of latest fragment

  uint8_t msg_len, *msg;      // length and payload of message
} PACKING_MACRO FRAGMENTBUFFER_ENTRY;

typedef struct {              // used for grouping physical fragments into one outgoing message
  uint8_t outputbuffer_index, confirm_seq, is_conf;
  ADDRESS dest_addr;
} PACKING_MACRO GROUPLIST_ENTRY;

typedef struct {
  uint8_t used;
  ADDRESS neighbor_node, extneighbor_node;
} PACKING_MACRO EXTNEIGHBORHOOD_ENTRY;

typedef struct {
  uint8_t used;
  ADDRESS id;
  uint32_t time;
  uint8_t count;
  int8_t rcv_power;
} PACKING_MACRO DISCOVERYBUFFER_ENTRY;

uint8_t time_hops;    // node whose time we are synchronizing to, initially local address, decreases

PEER_LINK peer_link[MAX_PEERLINKS];   // list of neighbors
uint8_t sendbroadcast_confirmseq;     // outgoing broadcast sequence
int16_t broadcast_msgindex;           // used to maintain in-order send of broadcast messages

ADDRESS msgout_destaddr;              // used internally during physical message integration
uint8_t msgout_groupsize;
GROUPLIST_ENTRY msgout_grouplist[MAX_GROUPSIZE];

OUTPUTBUFFER_ENTRY output_buffer[OUTPUTBUFFER_SIZE];  // physical outgoing queue
uint8_t outputbuffer_sending;                         // is OS currently sending
uint16_t outputbuffer_size, outputbuffer_maxindex;    // size and utilization of queue

OUTPUTSCHEDULE_ENTRY output_schedule[OUTPUTBUFFER_SIZE];    // scheduling priority queue for the physical outgoing queue
uint16_t outputschedule_size;                               // number of messages currently scheduled

FRAGMENTBUFFER_ENTRY fragment_buffer[FRAGMENTBUFFER_SIZE];  // incoming fragmented messages buffer
uint16_t fragmentbuffer_size;

EXTNEIGHBORHOOD_ENTRY extneighborhood_buffer[EXTNEIGHBORHOODBUFFER_SIZE];

DISCOVERYBUFFER_ENTRY discovery_buffer[DISCOVERYBUFFER_SIZE];

uint16_t channel_contention;          // for compatibility, use different contention model in TOSSIM vs. hardware       
uint8_t discoverymsg_sending;         // allow only one non-confirmed unicast or bcast discovery message to be sent at a time

uint8_t reliable_peercount;             // number of reliable peers discovered so far

#ifdef UNIT_TEST
int (*testhook_handlemessage) (uint8_t peer_index, uint8_t msg_len, uint8_t *msg) = NULL;
#endif

void init_mlink();

int handle_confirmation(uint8_t peer_index, uint8_t confirm_seq);

int8_t send_genericmsg(uint8_t *msg, uint8_t msg_len, uint8_t confirm_send, uint8_t node_count,
  ADDRESS *nodes, int16_t time_ofs, uint8_t send_power, uint8_t allow_merge);
void send_discoverymsg(ADDRESS addr, uint8_t confirm, uint8_t has_time, uint8_t time_sync);

int8_t update_buffer(ADDRESS *addr, uint8_t *msg_buf, uint8_t *msg_len, uint8_t *send_power);
void update_result(error_t result);

void handle_senddone(uint8_t was_acked, error_t result);
void decode_message(ADDRESS src_addr, ADDRESS dest_addr, uint8_t *msg_in, uint8_t msg_len, int8_t msg_power);

uint8_t peerlink_statusok(int peer_index);
int find_extneighborhoodentry(ADDRESS neighbor_node, ADDRESS extneighbor_node);

